home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Snippets / Sound / Cheap Studio / _source / Record_sound.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-15  |  16.7 KB  |  547 lines  |  [TEXT/CWIE]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    Routines demonstrating how to record sound asynchronously to disk.
  5. **
  6. **    by Mark Cookson, Apple Developer Technical Support
  7. **
  8. **    File:    Record_sound.c
  9. **
  10. **    Copyright ©1996 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "Apple Sample
  17. **    Code" after having made changes. If you're going to re-distribute the
  18. **    source, we require that you make it clear in the source that the code
  19. **    was descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. /*
  23.     This sample shows how to use SPBRecord to record to memory and then
  24.     write the recorded samples to disk, asynchronously, using PBWriteAsync.
  25.  
  26.     This sample is useful for those developers who wish more flexibility
  27.     than what is offered with SPBRecordToFile.
  28.  
  29.     This sample uses two parameter blocks because as Macs get faster, and
  30.     drives can't quite keep up, using just one parameter block for two writes
  31.     is just asking for trouble.  It's not that much extra memory and it will
  32.     be a weird bug you never have to find.  The pbInUse field of each parameter
  33.     block is supposed to keep us from reusing the parameter block in case things
  34.     get really stopped up.
  35.  
  36.     These routines are designed to be easliy portable (in fact they were ripped
  37.     from another sample), so you should be able to use them with little modification.
  38.  
  39.     It checks for errors returned from SPBRecord and from the PBWrites.  If there
  40.     is a disk error returned from a PBWrite the SPBRecord completion routine will
  41.     not start the next record, killing the recording process and the error will be
  42.     returned in the Vars structure (in the theErr field).
  43.  
  44.     This sample records three seconds of sound but doesn't call WaitNextEvent, so
  45.     don't worry, your Mac isn't locked up, just wait a few seconds.
  46.  
  47.     Happy recording!
  48. */
  49.  
  50. #include        <Quickdraw.h>
  51. #include        <Windows.h>
  52. #include        <dialogs.h>
  53. #include        <OSEvents.h>
  54. #include        <Memory.h>
  55. #include        <Packages.h>
  56. #include        <Sound.h>
  57. #include        <SoundInput.h>
  58. #include        <OSUtils.h>
  59. #include        <Files.h>
  60. #include        <StandardFile.h>
  61. #include        <Fonts.h>
  62.  
  63. //So we can pass our A5 and other info to our PBWrite completion routines
  64. typedef struct myParamBlockRec {
  65.     ParamBlockRec            pb;
  66.     long                    myA5;
  67.     Boolean                    pbInUse;
  68. }myParamBlockRec, *myParmBlkPtr;
  69.  
  70. //Keep track of the info needed to record
  71. typedef struct {
  72.         long            sanitycheck;
  73.         SPBPtr            recordRec;
  74.         myParmBlkPtr    pb0,
  75.                         pb1;
  76.         Ptr                recBuffer0,
  77.                         recBuffer1;
  78.         Fixed            sampleRate;
  79.         OSType            compression;
  80.         unsigned long    totalBytes;
  81.         long            myA5,
  82.                         devBuffer,
  83.                         soundRefNum;
  84.         short            whichBuffer,
  85.                         fileRefNum,
  86.                         numChannels,
  87.                         sampleSize;
  88.         OSErr            theErr;        //last error returned by SPBRecord or PBWrite
  89. } Vars, *VarsPtr;
  90.  
  91. OSErr            PrepairToRecordToDisk    (VarsPtr myVars);
  92. OSErr            RecordToDisk             (VarsPtr myVars);
  93. pascal void        MyRecComp                (SPBPtr inParamPtr);
  94. OSErr            FinishRecording         (VarsPtr myVars);
  95.  
  96. #if GENERATINGCFM
  97. pascal void        MyPB0WriteComp            (myParmBlkPtr passedPB);
  98. pascal void        MyPB1WriteComp            (myParmBlkPtr passedPB);
  99. #else 
  100. pascal void        MyPB0WriteComp            (myParmBlkPtr passedPB:__a0);
  101. pascal void        MyPB1WriteComp            (myParmBlkPtr passedPB:__a0);
  102. #endif
  103.  
  104. static void ToolBoxInit (void)
  105. {
  106.     MaxApplZone();
  107.     InitGraf (&qd.thePort);
  108.     InitFonts ();
  109.     InitWindows ();
  110.     InitMenus ();
  111.     TEInit ();
  112.     InitDialogs ((long)nil);
  113.     InitCursor ();
  114.     return;
  115. }
  116.  
  117. /*
  118. void main (void) {
  119.     Vars        myVars;
  120.     long        temp;
  121.     OSErr        err;
  122.  
  123.     ToolBoxInit ();
  124.  
  125.     err = PrepairToRecordToDisk (&myVars);
  126.     err = RecordToDisk (&myVars);
  127.  
  128.     Delay (180, &temp);        //record for about three seconds
  129.  
  130.     err = myVars.theErr;    //were any errors returned from the recording?
  131.     if (err != noErr && err != abortErr) {
  132.         DebugStr ("\pRecording didn't complete without error");
  133.     }
  134.  
  135.     //if the error was that we ran out of disk space we can still write the
  136.     //header because we preallocated space for it.  The file may be bad,
  137.     //but it doesn't have to be.  At any rate we want to close the file and
  138.     //dispose of memory.
  139.     err = FinishRecording (&myVars);
  140. }
  141. */
  142.  
  143. static OSErr    SetupDevice (long inputDevice,
  144.                             short *numChannels,
  145.                             short *sampleSize,
  146.                             Fixed *sampleRate,
  147.                             OSType *compression,
  148.                             long *devBuffer) {
  149.  
  150.     OSErr                err;
  151.     Fixed                gain = 0x00008000;
  152.     short                on = 1;
  153.  
  154.     err = SPBSetDeviceInfo (inputDevice, siSampleRate, (Ptr) sampleRate);
  155.     if (err != noErr)
  156.         DebugStr("\pcouldn't set sample rate");
  157.  
  158.     err = SPBSetDeviceInfo (inputDevice, siSampleSize, (Ptr) sampleSize);
  159.     if (err != noErr)
  160.         DebugStr("\pcouldn't set sample size");
  161.  
  162.     err = SPBSetDeviceInfo (inputDevice, siTwosComplementOnOff, (Ptr) &on);
  163.     if (err != noErr)
  164.         DebugStr("\pcouldn't set twos complement");
  165.  
  166.     err = SPBSetDeviceInfo (inputDevice, siNumberChannels, (Ptr) numChannels);
  167.     if (err != noErr)
  168.         DebugStr("\pcouldn't set number of channels");
  169.  
  170.     err = SPBSetDeviceInfo (inputDevice, siCompressionType, (Ptr) compression);
  171.     if (err != noErr)
  172.         DebugStr("\pcouldn't set compression type");
  173.  
  174.     //turn on continuous recording to "warm up" the input device
  175.     err = SPBSetDeviceInfo (inputDevice, siContinuous, (Ptr) &on);
  176.     if (err != noErr)
  177.         DebugStr("\pcouldn't turn on continuous recording");
  178.  
  179.     //turn on Automatic Gain Control
  180.     err = SPBSetDeviceInfo (inputDevice, siAGCOnOff, (Ptr) &on);
  181.     if (err != noErr) {
  182.         //If AGC isn't available, just turn it all the way down to avoid over driving
  183.         err = SPBSetDeviceInfo (inputDevice, siInputGain, (Ptr) &gain);
  184.         if (err != noErr)
  185.             DebugStr("\pcouldn't get siInputGain");
  186.     }
  187.  
  188.     //check to see what we really got
  189.     err = SPBGetDeviceInfo (inputDevice, siSampleRate, (Ptr) sampleRate);
  190.     if (err != noErr)
  191.         DebugStr("\pcouldn't get sample rate");
  192.  
  193.     err = SPBGetDeviceInfo (inputDevice, siSampleSize, (Ptr) sampleSize);
  194.     if (err != noErr)
  195.         DebugStr("\pcouldn't get sample size");
  196.  
  197.     err = SPBGetDeviceInfo (inputDevice, siNumberChannels, (Ptr) numChannels);
  198.     if (err != noErr)
  199.         DebugStr("\pcouldn't get number of channels");
  200.  
  201.     err = SPBGetDeviceInfo (inputDevice, siDeviceBufferInfo, (Ptr) devBuffer);
  202.     if (err != noErr)
  203.         DebugStr("\pcouldn't get number of channels");
  204.  
  205.     err = SPBGetDeviceInfo (inputDevice, siCompressionType, (Ptr) compression);
  206.     if (err != noErr)
  207.         DebugStr("\pcouldn't get compression type");
  208.  
  209.     return err;
  210. }
  211.  
  212. OSErr    PrepairToRecordToDisk (VarsPtr myVars) {
  213.     OSErr                err;
  214.     short                volRefNum;
  215.     long                buffSize, parID;
  216.     StandardFileReply    sfReply;
  217.     StringPtr            nameString;
  218.  
  219.     StandardPutFile ("\pPut recorded AIFF where...", "\pRecorded sound", &sfReply);
  220.     if (sfReply.sfGood) {
  221.         volRefNum = sfReply.sfFile.vRefNum;
  222.         parID = sfReply.sfFile.parID;
  223.         nameString = (StringPtr)&sfReply.sfFile.name;
  224.  
  225.         err = SPBOpenDevice (nil, siWritePermission, &myVars->soundRefNum);
  226.         if (err != noErr) {
  227.             DebugStr("\pcouldn't open the device");
  228.         }
  229.  
  230.         myVars->numChannels    = 2;
  231.         myVars->sampleSize    = 16;
  232.         myVars->sampleRate    = rate44khz;
  233.         myVars->compression    = 'NONE';
  234.         err = SetupDevice (myVars->soundRefNum, &myVars->numChannels,
  235.                                                 &myVars->sampleSize,
  236.                                                 &myVars->sampleRate,
  237.                                                 &myVars->compression,
  238.                                                 &myVars->devBuffer);
  239.  
  240.         buffSize = myVars->devBuffer * 20;    //make our buffer a multiple of the hardware's
  241.         myVars->recBuffer0 = NewPtrClear(buffSize);
  242.         if (MemError() != noErr || myVars->recBuffer0 == nil) {
  243.             DebugStr("\pcouldn't get memory for recording buffer");
  244.             err = memFullErr;
  245.         }
  246.  
  247.         myVars->recBuffer1 = NewPtrClear(buffSize);
  248.         if (MemError() != noErr || myVars->recBuffer1 == nil) {
  249.             DebugStr("\pcouldn't get memory for recording buffer");
  250.             err = memFullErr;
  251.         }
  252.  
  253.         if (err == noErr) {
  254.             myVars->recordRec = (SPBPtr) NewPtrClear(sizeof (SPB));
  255.             if (MemError() != noErr || myVars->recordRec == nil) {
  256.                 DebugStr("\pcouldn't get memory for recording record");
  257.                 err = memFullErr;
  258.             }
  259.         }
  260.  
  261.         if (err == noErr) {
  262.             myVars->pb0 = (myParmBlkPtr) NewPtrClear(sizeof (myParamBlockRec));
  263.             if (MemError() != noErr || myVars->pb0 == nil) {
  264.                 DebugStr("\pcouldn't get memory for param block");
  265.                 err = memFullErr;
  266.             }
  267.         }
  268.  
  269.         if (err == noErr) {
  270.             myVars->pb1 = (myParmBlkPtr) NewPtrClear(sizeof (myParamBlockRec));
  271.             if (MemError() != noErr || myVars->pb1 == nil) {
  272.                 DebugStr("\pcouldn't get memory for param block");
  273.                 err = memFullErr;
  274.             }
  275.         }
  276.  
  277.         if (err == noErr) {
  278.             HParamBlockRec        Hpb;
  279.             IOCompletionUPP     MyPB0WriteCompUPP,
  280.                                 MyPB1WriteCompUPP;
  281.             SICompletionUPP     MyRecCompUPP;
  282.  
  283.             myVars->sanitycheck = 'SANE';
  284.             myVars->myA5 = SetCurrentA5 ();
  285.             myVars->whichBuffer = 0;
  286.             myVars->pb0->myA5 = SetCurrentA5 ();
  287.             myVars->pb1->myA5 = SetCurrentA5 ();
  288.  
  289.             //set up the record parameters
  290.             MyRecCompUPP = NewSICompletionProc (MyRecComp);
  291.             myVars->recordRec->inRefNum = myVars->soundRefNum;
  292.             myVars->recordRec->count = buffSize;
  293.             myVars->recordRec->milliseconds = 0;
  294.             myVars->recordRec->bufferLength = buffSize;
  295.             myVars->recordRec->bufferPtr = myVars->recBuffer0;
  296.             myVars->recordRec->completionRoutine = MyRecCompUPP;
  297.             myVars->recordRec->interruptRoutine = nil;
  298.             myVars->recordRec->userLong = (long)myVars;
  299.             myVars->recordRec->error = 0;
  300.             myVars->recordRec->unused1 = 0;
  301.  
  302.             //create the file
  303.             Hpb.ioParam.ioCompletion = nil;
  304.             Hpb.ioParam.ioNamePtr = nameString;
  305.             Hpb.ioParam.ioVRefNum = volRefNum;
  306.             Hpb.fileParam.ioDirID = parID;
  307.             err = PBHCreateSync (&Hpb);
  308.             if (err == dupFNErr) {
  309.                 err = noErr;    //overwriting the file is not an error
  310.             }
  311.             if (err != noErr)
  312.                 DebugStr("\pPBHCreateSync failed");
  313.  
  314.             if (err == noErr) {
  315.                 //set the file type and creator
  316.                 Hpb.fileParam.ioVRefNum = volRefNum;
  317.                 Hpb.fileParam.ioDirID = parID;
  318.                 Hpb.fileParam.ioNamePtr = nameString;
  319.                 Hpb.fileParam.ioFVersNum = 0;
  320.                 Hpb.fileParam.ioFDirIndex = 0;
  321.                 err = PBHGetFInfoSync(&Hpb);
  322.                 if (err != noErr)
  323.                     DebugStr("\pPBHGetFInfoSync failed");
  324.             }
  325.             if (err == noErr) {
  326.                 Hpb.fileParam.ioFlFndrInfo.fdType = 'AIFF';
  327.                 Hpb.fileParam.ioFlFndrInfo.fdCreator = 'Csdo';
  328.                 Hpb.fileParam.ioDirID = parID;
  329.                 err = PBHSetFInfoSync (&Hpb);
  330.                 if (err != noErr)
  331.                     DebugStr("\pPBHSetFInfoSync failed");
  332.             }
  333.  
  334.             if (err == noErr) {
  335.                 //open the file for writing
  336.                 Hpb.ioParam.ioCompletion = nil;
  337.                 Hpb.ioParam.ioNamePtr = nameString;
  338.                 Hpb.ioParam.ioVRefNum = volRefNum;
  339.                 Hpb.ioParam.ioPermssn = fsRdWrPerm;
  340.                 Hpb.fileParam.ioDirID = parID;
  341.                 err = PBHOpenDFSync (&Hpb);
  342.                 if (err != noErr)
  343.                     DebugStr("\pPBHOpenDFSync failed");
  344.             }
  345.  
  346.             if (err == noErr) {
  347.                 myVars->fileRefNum = Hpb.ioParam.ioRefNum;
  348.  
  349.                 //set up the parameter blocks for the coming writes
  350.                 MyPB0WriteCompUPP = NewIOCompletionProc (MyPB0WriteComp);
  351.                 MyPB1WriteCompUPP = NewIOCompletionProc (MyPB1WriteComp);
  352.                 myVars->pb0->pb.ioParam.ioCompletion = MyPB0WriteCompUPP;
  353.                 myVars->pb0->pb.ioParam.ioVRefNum = volRefNum;
  354.                 myVars->pb0->pb.ioParam.ioRefNum = myVars->fileRefNum;
  355.                 myVars->pb0->pb.ioParam.ioPosMode = fsAtMark;
  356.  
  357.                 myVars->pb1->pb.ioParam.ioCompletion = MyPB1WriteCompUPP;
  358.                 myVars->pb1->pb.ioParam.ioVRefNum = volRefNum;
  359.                 myVars->pb1->pb.ioParam.ioRefNum = myVars->fileRefNum;
  360.                 myVars->pb1->pb.ioParam.ioPosMode = fsAtMark;
  361.  
  362.                 //write a temp AIFF header so file pointer for data is in the right place
  363.                 err = SetupAIFFHeader (myVars->fileRefNum, myVars->numChannels, myVars->sampleRate, myVars->sampleSize, myVars->compression, 0, 0);
  364.                 if (err != noErr)
  365.                     DebugStr("\pSetupAIFFHeader failed");
  366.             }
  367.         }
  368.     } else {
  369.         err = userCanceledErr;
  370.     }
  371.  
  372.     return err;
  373. }
  374.  
  375. OSErr RecordToDisk (VarsPtr myVars) {
  376.     OSErr            err = noErr;
  377.  
  378.     myVars->totalBytes = 0;
  379.     err = SPBRecord (myVars->recordRec, true);
  380.     if (err != noErr) {
  381.         DebugStr("\pSPBRecord failed");
  382.     }
  383.  
  384.     return err;
  385. }
  386.  
  387. /*
  388.     Stops the recording by calling SPBStopRecording and then writes the
  389.     correct file header to the file, closes the file and then disposes of
  390.     our pointers.
  391. */
  392. OSErr    FinishRecording (VarsPtr myVars) {
  393.     OSErr            err;
  394.     ParamBlockRec    pb;
  395.  
  396.     err = SPBStopRecording (myVars->soundRefNum);
  397.     if (err != noErr)
  398.         DebugStr("\pSPBStopRecording failed");
  399.  
  400.     err = SPBCloseDevice (myVars->soundRefNum);
  401.     if (err != noErr)
  402.         DebugStr("\pSPBCloseDevice failed");
  403.  
  404.     //Put the file pointer back to the start of the file to
  405.     //write the correct header over the temp header
  406.     pb.ioParam.ioCompletion = nil;
  407.     pb.ioParam.ioRefNum = myVars->fileRefNum;
  408.     pb.ioParam.ioPosMode = fsFromStart;
  409.     pb.ioParam.ioPosOffset = 0;
  410.     err = PBSetFPosSync (&pb);
  411.     if (err != noErr)
  412.         DebugStr("\pPBSetFPosSync failed");
  413.  
  414.     if (err == noErr) {
  415.         //write the header with the correct information
  416.         err = SetupAIFFHeader (myVars->fileRefNum, myVars->numChannels, myVars->sampleRate, myVars->sampleSize, myVars->compression, myVars->totalBytes, myVars->totalBytes/(myVars->sampleSize*myVars->numChannels));
  417.         if (err != noErr)
  418.             DebugStr("\pSetupAIFFHeader in FinishRecording failed");
  419.     }
  420.  
  421.     err = PBCloseSync (&pb);
  422.     if (err != noErr)
  423.         DebugStr("\pPBCloseSync failed");
  424.  
  425.     DisposePtr ((Ptr)myVars->recordRec);
  426.     DisposePtr (myVars->recBuffer0);
  427.     DisposePtr (myVars->recBuffer1);
  428.     DisposePtr ((Ptr)myVars->pb0);
  429.     DisposePtr ((Ptr)myVars->pb1);
  430.  
  431.     return err;
  432. }
  433.  
  434. /*
  435.     This gets called at the end of each record, it writes the recorded data to
  436.     disk (asynchronously) and then starts a new record using the same SPBPtr.
  437.  
  438.     Make sure you have continous recording turned on otherwise the recording
  439.     may loose data between calls to SPBRecord.    
  440. */
  441. pascal void MyRecComp (SPBPtr inParamPtr) {
  442.     VarsPtr            myVarsPtr;
  443.     OSErr            err;
  444.  
  445.     #if !GENERATINGCFM
  446.         long        oldA5;
  447.         oldA5 = SetA5 (((VarsPtr)inParamPtr->userLong)->myA5);
  448.     #endif
  449.  
  450.     myVarsPtr = (VarsPtr)inParamPtr->userLong;
  451.  
  452.     //setting this now will avoid a race condition of the record finishing before
  453.     //we can toggle which buffer to use
  454.     myVarsPtr->whichBuffer = !myVarsPtr->whichBuffer;
  455.  
  456.     //If the last write returned with no error then continue recording
  457.     if (myVarsPtr->pb0->pb.ioParam.ioResult == noErr && myVarsPtr->pb1->pb.ioParam.ioResult == noErr) {
  458.         //If we are aborting (stopping) the recording, we still want to write the last buffer
  459.         if (inParamPtr->error == noErr || inParamPtr->error == abortErr) {
  460.             if (myVarsPtr->whichBuffer == 1) {
  461.                 myVarsPtr->pb0->pb.ioParam.ioBuffer = myVarsPtr->recBuffer0;
  462.                 myVarsPtr->pb0->pb.ioParam.ioReqCount = myVarsPtr->recordRec->count;
  463.                 myVarsPtr->totalBytes += myVarsPtr->recordRec->count;
  464.                 if (myVarsPtr->pb0->pbInUse == false) {
  465.                     myVarsPtr->pb0->pbInUse = true;
  466.                     err = PBWriteAsync (&myVarsPtr->pb0->pb);
  467.                 }
  468.                 inParamPtr->bufferPtr = myVarsPtr->recBuffer1;
  469.                 if (inParamPtr->error == noErr) {
  470.                     err = SPBRecord (inParamPtr, true);
  471.                     if (err != noErr)
  472.                         DebugStr("\pSPBRecord1 failed");
  473.                 }
  474.             } else {
  475.                 myVarsPtr->pb1->pb.ioParam.ioBuffer = myVarsPtr->recBuffer1;
  476.                 myVarsPtr->pb1->pb.ioParam.ioReqCount = myVarsPtr->recordRec->count;
  477.                 myVarsPtr->totalBytes += myVarsPtr->recordRec->count;
  478.                 if (myVarsPtr->pb1->pbInUse == false) {
  479.                     myVarsPtr->pb1->pbInUse = true;
  480.                     err = PBWriteAsync (&myVarsPtr->pb1->pb);
  481.                 }
  482.                 inParamPtr->bufferPtr = myVarsPtr->recBuffer0;
  483.                 if (inParamPtr->error == noErr) {
  484.                     err = SPBRecord (inParamPtr, true);
  485.                     if (err != noErr)
  486.                         DebugStr("\pSPBRecord2 failed");
  487.                 }
  488.             }
  489.             myVarsPtr->theErr = err;
  490.         }
  491.     } else {
  492.         //There was an error from PBWrite, return the error in theErr of our structure
  493.         if (myVarsPtr->pb0->pb.ioParam.ioResult != noErr)
  494.             myVarsPtr->theErr = myVarsPtr->pb0->pb.ioParam.ioResult;
  495.         else myVarsPtr->theErr = myVarsPtr->pb1->pb.ioParam.ioResult;
  496.     }
  497.  
  498.     #if !GENERATINGCFM
  499.         oldA5 = SetA5 (oldA5);
  500.     #endif
  501.  
  502.     return;
  503. }
  504.  
  505. /*
  506.     These routines are here just for error checking in this example,
  507.     you may wish to update a status record or something similar.
  508.  
  509.     What do you do if you get a write error during async recording?
  510.     For this example we will simply terminate the recording process,
  511.     you should probably display an error.  Since the myParamBlockRec
  512.     doesn't contain the sound input device number we will check for
  513.     an error in the record completion routine and not start a new
  514.     recording if there was an error.  You can call SPBStopRecording
  515.     at interrupt time, but that isn't needed in this example.
  516.  
  517.     These routines display the error, but the record completion
  518.     routine checks the parameter blocks to make sure there was no
  519.     error before continuing the recording.
  520. */
  521. #if GENERATINGCFM
  522. pascal void MyPB0WriteComp (myParmBlkPtr passedPB)
  523. #else 
  524. pascal void MyPB0WriteComp (myParmBlkPtr passedPB:__a0)
  525. #endif
  526. {
  527.     if (passedPB->pb.ioParam.ioResult != noErr) {
  528.         DebugStr("\pPBWrite0 failed!");
  529.     }
  530.  
  531.     passedPB->pbInUse = false;
  532. }
  533.  
  534. #if GENERATINGCFM
  535. pascal void MyPB1WriteComp (myParmBlkPtr passedPB)
  536. #else 
  537. pascal void MyPB1WriteComp (myParmBlkPtr passedPB:__a0)
  538. #endif
  539. {
  540.     if (passedPB->pb.ioParam.ioResult != noErr) {
  541.         DebugStr("\pPBWrite1 failed!");
  542.     }
  543.  
  544.     passedPB->pbInUse = false;
  545. }
  546.  
  547.